<!---
 
  Copyright (c) 2010, Cristian Costantini - cristian@millemultimedia.it
  
  FileManager is licensed under the terms of the Open Source GPL 3.0 license. 
	
	http://www.gnu.org/licenses/gpl.html
  
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
--->
<cfcomponent output="false" hint="I'm a File Manager service plug-in. " extends="fileManager.util.AbsService">

	<cffunction name="init" access="public" output="false" returntype="FileManager">
        <cfargument name="fileIo" type="any" required="false" />
        <cfargument name="converter" type="any" required="false" />
		<cfscript>
        variables.fileIo = arguments.fileIo ;
		variables.separator = arguments.fileIo.getPathSeparator() ;
		variables.converter = arguments.converter;
		</cfscript>
		<cfreturn this />
	</cffunction>
	
	<!--- File system management --->
	
	<cffunction name="copyFile" access="public" output="false" returntype="struct" hint="Copy a file">
		<cfargument name="source" type="string" required="true" hint="Absolute file path of the file to copy">
		<cfargument name="destination" type="string" required="true" hint="Pathname of a directory where the file will be copied. If not an absolute path, it is relative to the source directory">
		<cfscript>
		var result = {};
		var fileName = "";
		var validator = "" ;
		
		arguments.source = getAbsolutePath( arguments.source );
		arguments.destination = getAbsolutePath( arguments.destination );
		
		try{
		
			validatePermission( "copyFile" );
			
			//variables.eventManager.notifyBeforeFileCopy( source=arguments.source, separator=variables.separator, destination=arguments.destination, validator=validator  ) ;
			
			variables.fileIo.copyFile( argumentCollection=arguments ) ;
			
			//variables.eventManager.notifyOnFileCopy( data = arguments ) ;
			
			result['success'] = true ;
		}	
		catch( 'application' cfcatch ){
			
			result['success'] = false ;
            result['type'] = cfcatch.type ;
            result['detail'] = cfcatch.detail ;
            result['message'] = cfcatch.message ;
        	
			variables.eventManager.notifyOnError( error = result ) ;
		}
		</cfscript>
		<cfreturn result />
	</cffunction>
	
	<cffunction name="copyDirectory" access="public" output="false" returntype="struct" hint="Copy a directory, including all its contents.">
		<cfargument name="source" type="string" required="true" hint="Absolute directory path">
		<cfargument name="destination" type="string" required="true" hint="Pathname of a directory where the file will be copied. If not an absolute path, it is relative to the source directory">
		<cfscript>
		var result = {};
		var directoryName = "";
		var validator = "" ;
		
		arguments.source = getAbsolutePath(  arguments.source );
		arguments.destination = getAbsolutePath( arguments.destination );
		
		try{
		
			validatePermission( "copyDirectory" );
		
			//variables.eventManager.notifyBeforeDirectoryCopy( source=arguments.source, separator=variables.separator, destination=arguments.destination, validator=validator ) ;
		
			variables.fileIo.copyDirectory( argumentCollection=arguments );
			
			//variables.eventManager.notifyOnDirectoryCopy( data = arguments ) ;
			
			result['success'] = true;
		}	
		catch( any cfcatch ){
		
				result['success'] = false;
                result['type'] = cfcatch.type;
                result['detail'] = cfcatch.detail;
                result['message'] = cfcatch.message;
            	
				variables.eventManager.notifyOnError( error = result );
		}
		</cfscript>
		<cfreturn result />
	</cffunction>
	
	<cffunction name="getIcoExtensionList" access="public" returntype="array" hint="Return a struct with icon of extension.">
		<cfscript>
		var fileList = "";
		var result = [];
		
		fileList = variables.fileIo.getFileList( expandPath( "/fileManager/assets/images/icon/" ) );
		
		for( i = 1; i <= fileList.recordCount; i++ ){
		
			result.add( fileList['name'][i] );
		
		}
		
		</cfscript>
		<cfreturn result />
	</cffunction>
	
	<cffunction name="getDiskSpace" access="public" returntype="array" hint="Return a struct with current disk space and max disk space.">
		<cfscript>
		var used = ceiling( variables.fileIo.getDirectorySize( getAbsolutePath(  getBasePath() ) ) / 1024 );
		var free = variables.sessionMgr.getValue('config').maxDiskSpace - used;
		var total = [];
		var validator = "" ;
		
		validatePermission( "getDiskSpace" );
		
		total[1]['space'] = ceiling( variables.fileIo.getDirectorySize( getAbsolutePath( getBasePath() ) ) / 1024 );
		total[1]['type'] = 'Used';
		total[2]['space'] = free;
		total[2]['type'] = 'Free';
		</cfscript>
		<cfreturn total />
	</cffunction>
	
	<cffunction name="getDirectories" access="public" output="false" returntype="Any" hint="Return query with column child for tree menu. If directory as children value is 1 else value is 0">
		<cfargument name="node" type="string" required="false" default="" />
		<cfargument name="col" type="string" required="false" default="" />
		<cfargument name="sort" type="string" required="false" default="" />
		<cfset var child = 1 />
		<cfset var listDir = queryNew("name, size, type, datelastmodified, attributes, mode, directory, fullpath, pathfrombase, canread, canwrite") />
		<cfset var childArray = [] />
		<cfset var relPathArray = [] />
        <cfset var result = {} />
		
		<cfset validatePermission( "getDirectories" ) />
		
		<cfif NOT len( arguments.node ) or arguments.node eq 'treeId'>
		
			<cfset arguments.node = getBasePath() />
		
		</cfif>
		
		<cfset listDir = variables.fileIo.getDirectoryList( getAbsolutePath(  "#arguments.node#" ) ) />
		
		<cfloop query="listDir">
			
			<cfset arrayAppend( childArray , variables.fileIo.getDirectoryList( fullPath ).recordCount GT 0 ) />
			<cfset arrayAppend( relPathArray , variables.separator & variables.fileIo.getRelativePath( getAbsolutePath(  getBasePath() ), fullPath ) ) />
			
		</cfloop>
		
		<cfset queryAddColumn( listDir, "child", childArray ) />
		<cfset queryAddColumn( listDir, "relativePath",  relPathArray ) />
		
		<!--- server side sort --->
		<cfquery name="listDir" dbtype="query">
		SELECT listDir.Name AS text, listDir.relativePath AS id
		FROM listDir
		WHERE listDir.Name NOT LIKE '%_trash%'
		<cfif arguments.col NEQ "" AND arguments.sort NEQ "">
		ORDER BY #arguments.col# #arguments.sort#
		</cfif>
		</cfquery>
		
		<!---<cfset variables.eventManager.notifyOnDirectoryListed( data = arguments ) />--->
			
		<cfreturn variables.converter.queryToArray( listDir ) />		
	</cffunction>
	
	<cffunction name="getFiles" access="public" output="false" returntype="Any" hint="Return query with column extension. The value of column is extension of file.">
		<cfargument name="node" type="string" required="false" default="" />
		<cfargument name="col" type="string" required="false" default="" />
		<cfargument name="sort" type="string" required="false" default="" />
		<cfargument name="start" type="string" required="false" default="0" />
		<cfargument name="limit" type="string" required="false" default="20" />
		<cfset var fileList = queryNew("name, size, type, datelastmodified, attributes, mode, directory, fullpath, pathfrombase, canread, canwrite") />
		<cfset var extArray = [] />
		<cfset var relPathArray = [] />
		<cfset var result = structNew() />
		<cfset var page = 1 />
		
		<cfset validatePermission( "getFiles" ) />
		
		<cfif NOT len( arguments.node ) or arguments.node eq 'treeId'>
		
			<cfset arguments.node = getBasePath() />
		
		</cfif>
		
		<cfset fileList = variables.fileIo.getFileList( getAbsolutePath(  "#arguments.node#" ) )/>
		
		<cfloop query="fileList">
			
			<cfset arrayAppend( extArray , lCase( listLast( name , '.' ) ) ) />
			<cfset arrayAppend( relPathArray , variables.separator & variables.fileIo.getRelativePath( getAbsolutePath(  getBasePath() ), fullPath ) ) />
			
		</cfloop>
		
		<cfset queryAddColumn( fileList, "extension", extArray ) />
		<cfset queryAddColumn( fileList, "relativePath", relPathArray ) />
		
		<!--- serverside sort --->
		<cfquery name="fileList" dbtype="query">
		SELECT *
		FROM fileList
		<cfif arguments.col NEQ "" AND arguments.sort NEQ "">
		ORDER BY '#arguments.col#' '#arguments.sort#'
		</cfif>
		</cfquery>
		
		<!--- fix  queryConvertForGrid() bug on CF8 --->
		<cfif fileList.RecordCount neq 0>
        
            <!--- if the last page contains less records than the limit, then change limit size to number remaining records --->
            <cfif fileList.RecordCount - arguments.start lt arguments.limit>
                <cfset arguments.limit = fileList.RecordCount - arguments.start />
            </cfif>
			
			<cfset page = ceiling( arguments.start/arguments.limit ) + 1>
    
        </cfif>
		
		<cfset result.query = variables.converter.queryToArray( queryConvertForGrid( fileList, page, arguments.limit ).query ) />
		
		<!---<cfset variables.eventManager.notifyOnFileListed( data = arguments ) />--->
	        
		<cfreturn result />
	</cffunction>
    
    <cffunction name="deleteDirectory" access="public" returntype="Struct" output="false" hint="Delete a directory, including all its contents."> 
		<cfargument name="directoryPath" type="string" required="true" /> 
		<cfargument name="throwIfMissing" type="boolean" required="false" default="true" /> 
	    <cfscript>
		var result = {};
		var validator = "" ;
		
		arguments.directoryPath = getAbsolutePath( arguments.directoryPath );
                    
        try{
		
			validatePermission( "deleteDirectory" );
		
			//variables.eventManager.notifyBeforeDirectoryDeleted( data = arguments );
            
            variables.fileIo.deleteDirectory( argumentCollection=arguments );
			
			//variables.eventManager.notifyOnDirectoryDeleted( data = arguments ) ;
            
            result['success'] = true;
        
		}
		catch( any cfcatch ){
	            
                result['success'] = false;
                result['type'] = cfcatch.type;
                result['detail'] = cfcatch.detail;
                result['message'] = cfcatch.message;
            	
				variables.eventManager.notifyOnError( error = result );
		}				
        </cfscript>
        <cfreturn result />
	</cffunction>
	
	<cffunction name="moveDirectory" access="public" returntype="Struct" output="false" hint="Move a directory, including all its contents."> 
		<cfargument name="source" type="string" required="true" /> 
		<cfargument name="destination" type="string" required="true" /> 
		<cfscript>
		var result = {};
        var directoryName = "";
		var validator = "" ;
		var directoryName = "";
		
		try{
		
			validatePermission( "moveDirectory" );
		
			if( NOT len( arguments.destination ) or reFindNoCase( "treeId", arguments.destination ) ){
			
				if( reFindNoCase( "treeId", arguments.destination ) ){
				
					directoryName = replaceNoCase( arguments.destination, "treeId", "", "one" );
				
				}
				
				arguments.destination = getBasePath() & directoryName;
			
			}
		
			arguments.source = getAbsolutePath(  arguments.source );
			arguments.destination = getAbsolutePath( arguments.destination );
			
			//variables.eventManager.notifyBeforeDirectoryMove( source=arguments.source, separator=variables.separator, destination=arguments.destination, validator=validator ) ;
		    
			if( not directoryExists( arguments.destination ) ){
			
            	variables.fileIo.moveDirectory( argumentCollection=arguments ) ;
			
			}
			
			//variables.eventManager.notifyOnDirectoryMoved( data = arguments ) ;
            
            result['success'] = true ;
       }     
       catch( any cfcatch ){
	            
                result['success'] = false ;
                result['type'] = cfcatch.type ;
                result['detail'] = cfcatch.detail ;
                result['message'] = cfcatch.message ;
            
				variables.eventManager.notifyOnError( error = result ) ;
			
        }
        </cfscript>
        <cfreturn result />
	</cffunction>
	
	<cffunction name="renameFile" access="public" returntype="Struct" output="false" > 
		<cfargument name="filePath" type="string" required="true" /> 
		<cfargument name="newFile" type="string" required="true" /> 
		<cfscript>
		var result = {};
        var validator = "" ;
		
		try{
		
			validatePermission( "renameFile" );
		
			if( NOT len( arguments.filePath ) ){
			
				arguments.filePath = getBasePath();
			
			}
		
			arguments.filePath = getAbsolutePath( arguments.filePath );
            
            variables.fileIo.renameFile( argumentCollection=arguments );
			
			//variables.eventManager.notifyOnFileRenamed( data = arguments );
            
            result['success'] = true;
       }     
       catch( any cfcatch ){
	            
                result['success'] = false;
                result['type'] = cfcatch.type;
                result['detail'] = cfcatch.detail;
                result['message'] = cfcatch.message;
            
				variables.eventManager.notifyOnError( error = result );
			
        }
        </cfscript>
        <cfreturn result />
	</cffunction>
	
	<cffunction name="moveFile" access="public" returntype="Struct" output="false" > 
		<cfargument name="source" type="string" required="true" /> 
		<cfargument name="destination" type="string" required="true" /> 
		<cfscript>
		var result = {};
		var fileName = "";
        var validator = "" ;
		
		if( NOT len( arguments.destination ) or reFindNoCase( "treeId", arguments.destination ) ){
		
			arguments.destination = getBasePath();
		
		}
		
		arguments.source = getAbsolutePath( arguments.source );
		arguments.destination = getAbsolutePath( arguments.destination );
		           
        try{
		
			validatePermission( "moveFile" );
		
			//variables.eventManager.notifyBeforeFileMove( source=arguments.source, separator=variables.separator, destination=arguments.destination, validator=validator ) ;
            
			if( not fileExists( arguments.destination ) ){
			
	            variables.fileIo.moveFile( argumentCollection=arguments );
			
			}
			
			//variables.eventManager.notifyOnFileMoved( data = arguments );
            
            result['success'] = true;
        }    
        catch( any cfcatch ){
	            
                result['success'] = false;
                result['type'] = cfcatch.type;
                result['detail'] = cfcatch.detail;
                result['message'] = cfcatch.message;
            
				variables.eventManager.notifyOnError( error = result );
			
        
        }
        </cfscript>
        <cfreturn result />
	</cffunction>
	
	<cffunction name="uploadFile" access="public" returntype="Struct" output="false"> 
		<cfargument name="fileField" type="string" required="true" /> 
		<cfargument name="destination" type="string" required="true" /> 
		<cfscript>
		var result = {};
		var upResult = "";
		var diskSpace = "";
		var validator = "" ;
		
		
		arguments.destination = getAbsolutePath( arguments.destination );
		arguments.mimetypes = variables.sessionMgr.getValue('config').mimetypes;
		arguments.maxsize = variables.sessionMgr.getValue('config').sizeLimit;
		arguments.nameconflict = variables.sessionMgr.getValue('config').nameconflict;
		
		try{
		
			validatePermission( "uploadFile" );
		            
        	upResult = variables.fileIo.uploadFile( argumentCollection=arguments );
			
			diskSpace = getDiskSpace();
			
			//variables.eventManager.notifyOnFileUploaded( source = upResult, diskspace= diskSpace, validator=validator );
			
			result['success'] = true;
			result['file'] = upResult;
        }     
        catch( any cfcatch ){
	            
                result['success'] = false;
                result['type'] = cfcatch.type;
                result['detail'] = cfcatch.detail;
                result['message'] = cfcatch.message;
				
				variables.fileIo.deleteFile( upResult );
            	
				variables.eventManager.notifyOnError( error = result );
				
        }
		</cfscript>
        <cfreturn result />
	</cffunction>
	
	<cffunction name="deleteFile" access="public" returntype="Struct" output="false" > 
		<cfargument name="filePath" type="string" required="true" /> 
		<cfscript>
		var result = {};
		var validator = "" ;
		
		arguments.filePath = getAbsolutePath( arguments.filePath );
		            
        try{
		
			validatePermission( "deleteFile" );
            
            variables.fileIo.deleteFile( argumentCollection=arguments );
			
			//variables.eventManager.notifyOnFileDeleted( data = arguments );
            
            result['success'] = true;
        }    
        catch( any cfcatch ){
	            
                result['success'] = false;
                result['type'] = cfcatch.type;
                result['detail'] = cfcatch.detail;
                result['message'] = cfcatch.message;
            	
				variables.eventManager.notifyOnError( error = result );
				
        }
        </cfscript>
        <cfreturn result />
	</cffunction>
	
	<cffunction name="renameDirectory" access="public" returntype="Struct" output="false" > 
		<cfargument name="directoryPath" type="string" required="true" /> 
		<cfargument name="newDirectory" type="string" required="true" /> 
		<cfscript>
		var result = {};
        var validator = "" ;
		
		arguments.directoryPath = getAbsolutePath( arguments.directoryPath );
		arguments.newDirectory = getAbsolutePath( arguments.newDirectory );
		            
        try{
		
			validatePermission( "renameDirectory" );
            
            variables.fileIo.renameDirectory( argumentCollection=arguments );
			
			//variables.eventManager.notifyOnDirectoryRenamed( data = arguments );
            
            result['success'] = true;
        }     
        catch( any cfcatch ){
	            
                result['success'] = false;
                result['type'] = cfcatch.type;
                result['detail'] = cfcatch.detail;
                result['message'] = cfcatch.message;
            
				variables.eventManager.notifyOnError( error = result );
			
        }
        </cfscript>
        <cfreturn result />
	</cffunction>
	
	<cffunction name="createDirectory" access="public" returntype="Struct" output="false" > 
		<cfargument name="directoryPath" type="string" required="true" /> 
		<cfscript>
		var result = {};
		var validator = "" ;
		var dirName = "";
		
		if( NOT len( arguments.directoryPath ) or reFindNoCase( 'treeId', arguments.directoryPath ) ){
		
			if( reFindNoCase( 'treeId', arguments.directoryPath ) ){
			
				dirName = replaceNoCase( arguments.directoryPath, "treeId/", "", "one" );
		
			}
			arguments.directoryPath = getBasePath() & dirName;
			
		}
		
		arguments.directoryPath = getAbsolutePath( arguments.directoryPath );  
		
        try{
		
			validatePermission( "createDirectory" );
			
			//variables.eventManager.notifyBeforeDirectoryCreate( source=arguments.source, separator=variables.separator, destination=arguments.destination, validator=validator ) ;
			
            variables.fileIo.createDirectory( arguments.directoryPath );
			
			//variables.eventManager.notifyOnDirectoryCreated( data = arguments );
            
            result['success'] = true;
         }   
         catch( any cfcatch ){
	            
                result['success'] = false;
                result['type'] = cfcatch.type;
                result['detail'] = cfcatch.detail;
                result['message'] = cfcatch.message;
				
				variables.eventManager.notifyOnError( error = result );
            
        }
        </cfscript>
        <cfreturn result />
	</cffunction>
	
	<!--- 
	Special thanks to: Fabio Serra
	--->
	<cffunction name="download" access="public" returntype="void" output="true">
		<cfargument name="download" type="string" required="true"/>
		<cfset var fileName = getAbsolutePath( "/" & arguments.download  )/>
		
		<cfset validatePermission( "download" ) />
		
		<cfheader name="content-length" value="#getFileInfo( fileName ).size#" />
		<cfheader name="Content-Disposition" value="attachment; filename=#getFileFromPath( fileName )#" />
		<cfcontent type="unknown/unknown" file="#fileName#" />
	</cffunction>

</cfcomponent>